#include "Cliente.h"

string etiqueta;

Cliente::Cliente(string nombre):sem(FuncionesComunes::ConvertirACharPointer(ARCHIVO_SEMAFORO), 1)
,log(FuncionesComunes::GetNombreLog()){
	id = getpid();
	this->nombre = nombre;
	etiqueta = "[Cliente " + nombre + "] ";
	canalServidor = new Fifo(FuncionesComunes::GetFifoServidor().c_str());
	SignalHandler::getInstance()->registrarHandler(SIGINT, &sigint_handler);
}


void Cliente::activarModoDebug(bool activar){
	log.activeDebug(activar);
}

void Cliente::ejecutar(){

	escribirLog("Ejecutando cliente: " + nombre);
	pathFifo = FuncionesComunes::getFifoCliente(FuncionesComunes::ConvertirAString(id)) + nombre;
	canalCliente = new Fifo(pathFifo.c_str());
	try {
		conectarConServidor();

		int choice;
		cout << " ===== Menu ===== " << endl;
		cout << "1. Iniciar nueva conversación" << endl;
		cout << "2. Unirse a conversación existente" << endl;
		cout << "3. Consultar conversaciones disponibles" << endl;
		cout << "4. Consultar usuarios existentes en conversación actual" << endl;
		cout << "5. Salir de la conversación actual" << endl;
		cout << "0. Salir" << endl;

		do {
			cin >> choice;
			switch (choice){
			case 1:
				escribirLog("Nueva conversacion");
				nuevaConversacion();
				break;
			case 2:
				int idConversacion;
				cout << "Ingrese el identificador de la conversación a la que desea unirse:" << endl;
				cin >> idConversacion;
				unirseAConv(idConversacion);
				break;
			case 3:
				cout << "Conversaciones disponibles" << endl;
				mostrarConvDisp();
				break;
			case 4:
				cout << "Usuarios en conversacion actual" << endl;
				mostrarUsuarios();
				break;
			case 5:
				cout << "Salir de la conversacion actual" << endl;
				abandonarConversacion();
				break;
			case 0:FuncionesComunes::GetFifoServidor() +  nombre + FuncionesComunes::ConvertirAString(id);
				break;
			default:
				cout << "Seleccione un numero del 0 al 5" << endl;
			}
		}
		while (choice != 0 && sigint_handler.getGracefulQuit() == 0);
		desconectarConServidor();
	} catch ( const char* mensaje ) {
		escribirLog(mensaje);
	}
}


void Cliente::conectarConServidor(){
	Mensaje msjCnx (nombre,NOMBRE_SERVIDOR,CONECTAR, FuncionesComunes::getFifoCliente(FuncionesComunes::ConvertirAString(id)) + nombre);
	msjCnx.setIdCliente(id);
	char buffRta [ BUFFSIZE ];

	escribirLog("Abriendo conexión con el servidor");
	escribirLog("Se envía el mensaje: " + msjCnx.toString());
	canalServidor->escribir(FuncionesComunes::ConvertirACharPointer(msjCnx.toString()), strlen(FuncionesComunes::ConvertirACharPointer(msjCnx.toString())));

	int longRta = canalCliente->leer(buffRta, BUFFSIZE);
	escribirLog("Lei del fifo: " + FuncionesComunes::ConvertirAString(longRta));
	buffRta [ longRta ] = '\0';
	escribirLog("Recibiendo mensaje");
	Mensaje msjCnxRta (buffRta);
	if(msjCnxRta.getTipo() == OK) escribirLog("Se conectó exitosamente");
}

void Cliente::desconectarConServidor(){
	Mensaje cerrarCnx (nombre, NOMBRE_SERVIDOR, DESCONECTAR, MENSAJE_VACIO);

	escribirLog("Cerrando la conexion");
	canalServidor->escribir(FuncionesComunes::ConvertirACharPointer(cerrarCnx.toString()), strlen(FuncionesComunes::ConvertirACharPointer(cerrarCnx.toString())));
	escribirLog("El cliente " + nombre + " se desconecta del servidor.");
	canalCliente->cerrar();
	canalServidor->cerrar();
	SignalHandler::getInstance()->destruir();
}

/**
 * Crea una nueva conversación. Para esto, se debe crear un mensaje del tipo Nueva conversacion
 * y se debe llegar el ok del servidor.
 */
void Cliente::nuevaConversacion(){
	Mensaje msj (nombre, NOMBRE_SERVIDOR, INICIAR_CONVERSACION, MENSAJE_VACIO);
	string nombre;
	char buffRta [ BUFFSIZE ];

	cout<<"Ingrese el nombre de la conversación"<<endl;
	cin>>nombre;
	msj.setMensaje(nombre);

	//Envío el mensaje del tipo INICIAR_CONVERSACION
	escribirLog("Enviando mensaje nueva Conversacion");
	escribirLog("El mensaje a enviar es: " + msj.toString());
	canalServidor->escribir(FuncionesComunes::ConvertirACharPointer(msj.toString()), strlen(FuncionesComunes::ConvertirACharPointer(msj.toString())));

	//Espero el OK para dar de alta la conversación.
	int longRta = canalCliente->leer(buffRta, BUFFSIZE);
	buffRta [ longRta ] = '\0';
	escribirLog("Recibiendo respuesta al mensaje nueva Conversacion");
	Mensaje msjCnxRta (buffRta);
	if(msjCnxRta.getTipo() == OK) {
		escribirLog("Se creó la conversación exitosamente");
		idConversacion = msjCnxRta.getIdConversacion();
		nombreConversacion = nombre;
		empezarConversacion();
	}
	else escribirLog(msjCnxRta.getMensaje());
}

void Cliente::empezarConversacion(){

		pid_t pid = fork();

		if (pid == 0)leerMensajes();
		else escribirMensajes();

		kill(pid,SIGINT);
		wait(NULL);
}


void Cliente::leerMensajes(){
	while (sigint_handler.getGracefulQuit() == 0) {
		char buffRta [BUFFSIZE];
		escribirLog("Esperando para leer");
		int bytesLeidos = canalCliente->leer( buffRta, BUFFSIZE);
		escribirLog("Recibiendo respuesta");
		buffRta [bytesLeidos] = '\0';
		Mensaje msj (buffRta);
		escribirLog("Leyendo el mensaje" + msj.getMensaje());
		cout<<msj.getMensaje()<<endl;
	}
}

void Cliente::escribirMensajes(){
	cout<<"Escribir mensaje: "<<endl;
	cout<<"[ " + nombre +" dice: ]"<<endl;
		while (sigint_handler.getGracefulQuit() == 0) {
			char linea[100];
			cin.getline(linea, 100);
			string s (linea);
			if(!s.empty()){
				string contenido = "[ " + nombre + " dice ]: " + s;
				Mensaje msj ( nombre, nombreConversacion, CONVERSAR, contenido );
				escribirLog("Escribió: " + s);
				canalServidor->escribir(FuncionesComunes::ConvertirACharPointer(msj.toString()), strlen(FuncionesComunes::ConvertirACharPointer(msj.toString())));
			}
	}
}

void Cliente::unirseAConv(int idConv){
	char buffRta [ BUFFSIZE ];

	//Armando mensaje para unirse a una conversación
	Mensaje m (nombre, NOMBRE_SERVIDOR, UNIRSE_CONV, MENSAJE_VACIO);
	m.setIdCliente(this->id);
	m.setIdConversacion(idConv);
	escribirLog("El mensaje a enviar es: " + m.toString());
	canalServidor->escribir(FuncionesComunes::ConvertirACharPointer(m.toString()),strlen(FuncionesComunes::ConvertirACharPointer(m.toString())));

	//Recibiendo la respuesta
	int byteLeidos = canalCliente->leer( buffRta, BUFFSIZE);
	buffRta[byteLeidos] = '\0';
	Mensaje rta (buffRta);
	if(rta.getTipo() == OK){
		escribirLog("Respuesta de union a conversacion: OK");
		tieneConversacion = true;
		empezarConversacion();
	}
}

void Cliente::mostrarConvDisp(){
	char buffRta [BUFFSIZE];

	//Armando mensaje para el pedido del listado de conversaciones
	Mensaje m (nombre, NOMBRE_SERVIDOR, CONV_ACTIVAS, MENSAJE_VACIO);
	escribirLog("Mostrar conversaciones disponibles");
	escribirLog("El mensaje a enviar es: " + m.toString());
	canalServidor->escribir(FuncionesComunes::ConvertirACharPointer(m.toString()),strlen(FuncionesComunes::ConvertirACharPointer(m.toString())));

	//Recibiendo la respuesta
	int byteLeidos = canalCliente->leer( buffRta, BUFFSIZE);
	buffRta[byteLeidos] = '\0';
	Mensaje rta (buffRta);
	if(rta.getTipo() == OK){
		escribirLog("Mostrando las conversaciones disponibles");
		escribirLog(rta.toString());
		cout<<"Las conversaciones disponibles son: "<<rta.toString()<<endl;
	}
}


void Cliente::mostrarUsuarios(){
	char buffRta[ BUFFSIZE ];
	if(tieneConversacion){
		escribirLog("Pedido de usuarios en conversacion");

		//Armando mensaje para el pedido del listado de usuarios
		Mensaje m (nombre, NOMBRE_SERVIDOR, PARTIC_ACTIVOS, MENSAJE_VACIO);
		m.setIdConversacion(idConversacion);
		m.setIdCliente(id);
		escribirLog("El mensaje a enviar es: " + m.toString());
		canalServidor->escribir(FuncionesComunes::ConvertirACharPointer(m.toString()),strlen(FuncionesComunes::ConvertirACharPointer(m.toString())));

		//Recibiendo la respuesta
		int byteLeidos = canalCliente->leer( buffRta, BUFFSIZE);
		buffRta[byteLeidos] = '\0';
		Mensaje rta (buffRta);
		if(rta.getTipo() == OK){
			escribirLog("Mostrando los usuarios conectados");
			escribirLog(rta.toString());
			cout<<"Usuarios conectados: "<<rta.toString()<<endl;
		}
	}
	else escribirLog("El cliente no está en ninguna conversación");
}

void Cliente::abandonarConversacion(){
	escribirLog("Pedido de abandonar conversacion");
	if(tieneConversacion){
		escribirLog("El cliente puede abandonar la conversación" + idConversacion);
		Mensaje m (nombre, NOMBRE_SERVIDOR, DEJAR_CONV, MENSAJE_VACIO);
		m.setIdConversacion(idConversacion);
		m.setIdCliente(id);
		escribirLog("El mensaje a enviar es: " + m.toString());
		canalServidor->escribir(FuncionesComunes::ConvertirACharPointer(m.toString()), strlen(FuncionesComunes::ConvertirACharPointer(m.toString())));
	}
	else escribirLog("El cliente no está en ninguna conversación");
}


void Cliente::escribirLog(string mensaje){
	log.escribir(etiqueta + mensaje + "\n");
}

Cliente::~Cliente() {
	delete canalServidor;
	delete canalCliente;
}
